package com.pt25.service.sys.domain.codegen.service.impl;

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.pt25.base.dep.db.po.BasePo;
import com.pt25.base.dep.db.po.TreePo;
import com.pt25.base.model.security.aspect.PermissionCheck;
import com.pt25.base.res.ResBody;
import com.pt25.base.util.JsonUtil;
import com.pt25.base.util.PageUtil;
import com.pt25.base.util.StrUtil;
import com.pt25.base.util.TreeUtil;
import com.pt25.service.sys.domain.codegen.ddo.Codegen;
import com.pt25.service.sys.domain.codegen.ddo.ColumnInfo;
import com.pt25.service.sys.domain.codegen.ddo.TableInfo;
import com.pt25.service.sys.domain.codegen.service.CodegenService;
import com.squareup.javapoet.*;
import freemarker.cache.ClassTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import lombok.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.*;

import javax.lang.model.element.Modifier;
import java.io.*;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.sql.*;
import java.time.LocalDateTime;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

@Slf4j
@Service
public class CodegenServiceImpl implements CodegenService {

    @Override
    public void genCode(Codegen codegen) {
        genPo(codegen);
        genMapper(codegen);
        genRepo(codegen);
        genRepoImpl(codegen);
        genDo(codegen);
        genService(codegen);
        genServiceImpl(codegen);
        genVo(codegen);
        genController(codegen);
        genPage(codegen);
    }


    private void genPo(Codegen codegen) {
        // 获取数据表信息
        TableInfo tableInfo = getTableInfo(codegen);

        // 添加包名
        String packageName = codegen.getBasePackage() + ".repository.mysql.po";

        // 添加类名
        String uppercaseModelName = createUppercaseName(codegen.getModelName());

        //添加注解
        List<AnnotationSpec> annotations = getAnnotations(true);
        annotations.add(AnnotationSpec.builder(TableName.class).addMember("value", "$S", tableInfo.getTableName()).build());

        // 添加父类
        TypeName superClass;
        if (tableInfo.getTreeData()) {
            superClass = ParameterizedTypeName.get(TreePo.class);
        } else {
            superClass = ParameterizedTypeName.get(BasePo.class);
        }

        //添加接口
        TypeName superInterface = ParameterizedTypeName.get(Serializable.class);


        //添加字段
        List<FieldSpec> fields = new ArrayList<>();
        fields.add(FieldSpec.builder(String.class, "key", Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC)
                .initializer("$S", tableInfo.getTableName())
                .addAnnotation(AnnotationSpec.builder(TableField.class).addMember("exist", "$L", false).build()).build());
        for (ColumnInfo columnInfo : tableInfo.getColumnInfos()) {
            if (filterColumnName(columnInfo.getColumnName())) {
                if (columnInfo.getColumnType().toUpperCase().contains("BIGINT")) {
                    fields.add(FieldSpec.builder(returnFieldType(columnInfo.getColumnType()), createLowercaseName(columnInfo.getColumnName()), Modifier.PRIVATE)
                            .addJavadoc("$L", columnInfo.getColumnComment())
                            .addAnnotation(AnnotationSpec.builder(JsonFormat.class).addMember("shape", "$T.Shape.STRING", JsonFormat.class).build())
                            .addAnnotation(AnnotationSpec.builder(TableField.class).addMember("value", "$S", columnInfo.getColumnName()).build())
                            .build());

                } else {
                    fields.add(FieldSpec.builder(returnFieldType(columnInfo.getColumnType()), createLowercaseName(columnInfo.getColumnName()), Modifier.PRIVATE)
                            .addJavadoc("$L", columnInfo.getColumnComment())
                            .addAnnotation(AnnotationSpec.builder(TableField.class).addMember("value", "$S", columnInfo.getColumnName()).build())
                            .build());
                }
            }
        }

        // 创建类
        TypeSpec.Builder builder = TypeSpec.classBuilder(uppercaseModelName + "Po")
                .addModifiers(Modifier.PUBLIC)
                .addFields(fields)
                .addSuperinterface(superInterface)
                .superclass(superClass)
                .addAnnotations(annotations);
        TypeSpec typeSpec = builder.build();

        saveFile(codegen, packageName, typeSpec);
    }

    private void genMapper(Codegen codegen) {
        // 添加包名
        String packageName = codegen.getBasePackage() + ".repository.mysql.mapper";

        // 添加类名
        String uppercaseModelName = createUppercaseName(codegen.getModelName());

        // 添加注解
        AnnotationSpec annotationSpec = AnnotationSpec.builder(Mapper.class).build();

        //添加父类
        ParameterizedTypeName superInterface = ParameterizedTypeName.get(
                ClassName.get(BaseMapper.class),
                ClassName.get(codegen.getBasePackage() + ".repository.mysql.po", uppercaseModelName + "Po"));


        // 创建类
        TypeSpec.Builder builder = TypeSpec.interfaceBuilder(uppercaseModelName + "Mapper")
                .addModifiers(Modifier.PUBLIC)
                .addSuperinterface(superInterface)
                .addAnnotation(annotationSpec);
        TypeSpec typeSpec = builder.build();

        saveFile(codegen, packageName, typeSpec);
    }

    private void genRepo(Codegen codegen) {
        // 获取数据表信息
        TableInfo tableInfo = getTableInfo(codegen);

        // 添加包名
        String packageName = codegen.getBasePackage() + ".repository.mysql.persistence";

        // 添加类名
        String uppercaseModelName = createUppercaseName(codegen.getModelName());
        String lowercaseModelName = createLowercaseName(codegen.getModelName());

        //添加父类
        ParameterizedTypeName superInterface = ParameterizedTypeName.get(
                ClassName.get(IService.class),
                ClassName.get(codegen.getBasePackage() + ".repository.mysql.po", uppercaseModelName + "Po"));

        //添加方法
        List<MethodSpec> methodSpecs = new ArrayList<>();
        ClassName doClassName = ClassName.get(codegen.getBasePackage() + ".domain." + codegen.getDomain() + ".ddo", uppercaseModelName);
        if (!tableInfo.getTreeData()) {
            MethodSpec page = MethodSpec.methodBuilder("page" + uppercaseModelName)
                    .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
                    .addParameter(doClassName, lowercaseModelName)
                    .returns(ParameterizedTypeName.get(PageUtil.PageVo.class))
                    .build();
            methodSpecs.add(page);
        }

        // 创建类
        TypeSpec.Builder builder = TypeSpec.interfaceBuilder(uppercaseModelName + "Repo")
                .addModifiers(Modifier.PUBLIC)
                .addSuperinterface(superInterface)
                .addMethods(methodSpecs);
        TypeSpec typeSpec = builder.build();

        saveFile(codegen, packageName, typeSpec);
    }

    private void genRepoImpl(Codegen codegen) {
        // 获取数据表信息
        TableInfo tableInfo = getTableInfo(codegen);
        // 添加包名
        String packageName = codegen.getBasePackage() + ".repository.mysql.persistence.impl";

        // 添加类名
        String uppercaseModelName = createUppercaseName(codegen.getModelName());
        String lowercaseModelName = createLowercaseName(codegen.getModelName());

        // 添加注解
        AnnotationSpec annotationSpec = AnnotationSpec.builder(Service.class).build();

        // 添加父类
        ParameterizedTypeName superClass = ParameterizedTypeName.get(
                ClassName.get(ServiceImpl.class),
                ClassName.get(codegen.getBasePackage() + ".repository.mysql.mapper", uppercaseModelName + "Mapper"),
                ClassName.get(codegen.getBasePackage() + ".repository.mysql.po", uppercaseModelName + "Po"));
        // 添加接口
        ClassName superInterface = ClassName.get(codegen.getBasePackage() + ".repository.mysql.persistence", uppercaseModelName + "Repo");

        // 添加方法
        List<MethodSpec> methodSpecs = new ArrayList<>();
        ClassName doClassName = ClassName.get(codegen.getBasePackage() + ".domain." + codegen.getDomain() + ".ddo", uppercaseModelName);
        ClassName poClassName = ClassName.get(codegen.getBasePackage() + ".repository.mysql.po", uppercaseModelName + "Po");
        if (!tableInfo.getTreeData()) {
            MethodSpec.Builder builder = MethodSpec.methodBuilder("page" + uppercaseModelName);
            builder.addAnnotation(Override.class);
            builder.addModifiers(Modifier.PUBLIC);
            builder.addParameter(doClassName, lowercaseModelName);
            builder.returns(ParameterizedTypeName.get(PageUtil.PageVo.class));
            builder.addStatement("$T<$T> page = new $T<>(" + lowercaseModelName + ".getPageNum(), " + lowercaseModelName + ".getPageSize())",
                    IPage.class,
                    poClassName,
                    Page.class);
            builder.addStatement("$T<$T> queryWrapper = new $T<>()",
                    QueryWrapper.class,
                    poClassName,
                    QueryWrapper.class);
            builder.addStatement("queryWrapper.lambda().eq($T::getDelFlag, 0)", poClassName);
            builder.addStatement("page(page, queryWrapper)");
            builder.addStatement("""
                            return $T.builder()
                            .total(page.getTotal())
                            .pageNum(page.getCurrent())
                            .pageSize(page.getSize())
                            .list(page.getRecords())
                            .build()""",
                    ParameterizedTypeName.get(PageUtil.PageVo.class));
            MethodSpec page = builder.build();
            methodSpecs.add(page);
        }

        // 创建类
        TypeSpec.Builder builder = TypeSpec.classBuilder(uppercaseModelName + "RepoImpl")
                .addModifiers(Modifier.PUBLIC)
                .addAnnotation(annotationSpec)
                .superclass(superClass)
                .addSuperinterface(superInterface)
                .addMethods(methodSpecs);
        TypeSpec typeSpec = builder.build();

        saveFile(codegen, packageName, typeSpec);
    }


    private void genDo(Codegen codegen) {
        //获取数据表信息
        TableInfo tableInfo = getTableInfo(codegen);

        // 添加包名
        String packageName = codegen.getBasePackage() + ".domain." + codegen.getDomain() + ".ddo";

        // 添加类名
        String uppercaseModelName = createUppercaseName(codegen.getModelName());
        String lowercaseModelName = createLowercaseName(codegen.getModelName());

        //添加注解
        List<AnnotationSpec> annotations = getAnnotations(true);

        // 添加父类
        TypeName superClass;
        if (tableInfo.getTreeData()) {
            superClass = ParameterizedTypeName.get(TreeUtil.Tree.class);
        } else {
            superClass = ParameterizedTypeName.get(PageUtil.PageBase.class);
        }

        //添加字段
        List<FieldSpec> fields = createFields(tableInfo);

        //添加po转do方法
        Class<JsonUtil> jsonUtilClass = JsonUtil.class;
        ClassName poClassName = ClassName.get(codegen.getBasePackage() + ".repository.mysql.po", uppercaseModelName + "Po");
        MethodSpec toPo = MethodSpec.methodBuilder("to" + uppercaseModelName + "Po")
                .returns(poClassName)
                .addModifiers(Modifier.PUBLIC)
                .addStatement("return $T.toBean($T.toJson(this), $T.class)", jsonUtilClass, jsonUtilClass, poClassName)
                .build();

        //添加po转do方法
        ClassName doClassName = ClassName.get(packageName, uppercaseModelName);
        MethodSpec toDo = MethodSpec.methodBuilder("to" + uppercaseModelName)
                .returns(doClassName)
                .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                .addParameter(poClassName, lowercaseModelName + "Po")
                .addStatement("return $T.toBean($T.toJson(" + lowercaseModelName + "Po), $T.class)", jsonUtilClass, jsonUtilClass, doClassName)
                .build();

        // 创建类
        TypeSpec.Builder builder = TypeSpec.classBuilder(uppercaseModelName)
                .addModifiers(Modifier.PUBLIC)
                .addFields(fields)
                .addMethods(Arrays.asList(toPo, toDo))
                .addAnnotations(annotations);
        if (null != superClass) {
            builder.superclass(superClass);
        }
        TypeSpec typeSpec = builder.build();

        saveFile(codegen, packageName, typeSpec);
    }

    private void genService(Codegen codegen) {
        //获取数据表信息
        TableInfo tableInfo = getTableInfo(codegen);
        // 添加包名
        String packageName = codegen.getBasePackage() + ".domain." + codegen.getDomain() + ".service";

        // 添加类名
        String uppercaseModelName = createUppercaseName(codegen.getModelName());
        String lowercaseModelName = createLowercaseName(codegen.getModelName());


        // 添加方法列表
        ClassName doClassName = ClassName.get(codegen.getBasePackage() + ".domain." + codegen.getDomain() + ".ddo", uppercaseModelName);
        MethodSpec add = MethodSpec.methodBuilder("add" + uppercaseModelName)
                .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
                .addParameter(doClassName, lowercaseModelName)
                .returns(Long.class)
                .build();

        MethodSpec del = MethodSpec.methodBuilder("del" + uppercaseModelName)
                .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
                .addParameter(ClassName.get(Long.class), "id")
                .returns(Boolean.class)
                .build();

        MethodSpec update = MethodSpec.methodBuilder("update" + uppercaseModelName)
                .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
                .addParameter(doClassName, lowercaseModelName)
                .returns(Boolean.class)
                .build();


        MethodSpec get = MethodSpec.methodBuilder("get" + uppercaseModelName)
                .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
                .addParameter(ClassName.get(Long.class), "id")
                .returns(doClassName)
                .build();


        List<MethodSpec> methodSpecs = new ArrayList<>();
        methodSpecs.add(add);
        methodSpecs.add(del);
        methodSpecs.add(update);
        methodSpecs.add(get);

        if (tableInfo.getTreeData()) {
            ClassName list = ClassName.get(List.class);
            TypeName listOfConfigType = ParameterizedTypeName.get(list, doClassName);
            MethodSpec listMethod = MethodSpec.methodBuilder("list" + uppercaseModelName)
                    .returns(listOfConfigType)
                    .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
                    .build();
            methodSpecs.add(listMethod);
        } else {
            MethodSpec page = MethodSpec.methodBuilder("page" + uppercaseModelName)
                    .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
                    .addParameter(doClassName, lowercaseModelName)
                    .returns(ParameterizedTypeName.get(PageUtil.PageVo.class))
                    .build();
            methodSpecs.add(page);
        }

        // 创建类
        TypeSpec.Builder builder = TypeSpec.interfaceBuilder(uppercaseModelName + "Service")
                .addModifiers(Modifier.PUBLIC)
                .addMethods(methodSpecs);
        TypeSpec typeSpec = builder.build();

        saveFile(codegen, packageName, typeSpec);
    }

    private void genServiceImpl(Codegen codegen) {
        //获取数据表信息
        TableInfo tableInfo = getTableInfo(codegen);
        // 添加包名
        String packageName = codegen.getBasePackage() + ".domain." + codegen.getDomain() + ".service.impl";

        // 添加类名
        String uppercaseModelName = createUppercaseName(codegen.getModelName());
        String lowercaseModelName = createLowercaseName(codegen.getModelName());

        // 添加注解
        AnnotationSpec annotationSpec = AnnotationSpec.builder(Service.class).build();

        // 添加父类
        ClassName superInterface = ClassName.get(codegen.getBasePackage() + ".domain." + codegen.getDomain() + ".service", uppercaseModelName + "Service");

        // 添加字段
        FieldSpec fieldSpec = FieldSpec.builder(ClassName.get(codegen.getBasePackage() + ".repository.mysql.persistence", uppercaseModelName + "Repo"), lowercaseModelName + "Repo", Modifier.PRIVATE)
                .addAnnotation(ClassName.get(Resource.class))
                .build();

        // 添加方法
        ClassName doClassName = ClassName.get(codegen.getBasePackage() + ".domain." + codegen.getDomain() + ".ddo", uppercaseModelName);
        ClassName poClassName = ClassName.get(codegen.getBasePackage() + ".repository.mysql.po", uppercaseModelName + "Po");
        MethodSpec add = MethodSpec.methodBuilder("add" + uppercaseModelName)
                .addAnnotation(Override.class)
                .addModifiers(Modifier.PUBLIC)
                .addParameter(doClassName, lowercaseModelName)
                .addStatement("$T " + lowercaseModelName + "Po = " + lowercaseModelName + ".to" + uppercaseModelName + "Po()", poClassName)
                .addStatement(lowercaseModelName + "Repo.save(" + lowercaseModelName + "Po)")
                .addStatement("return " + lowercaseModelName + "Po.getId()")
                .returns(Long.class)
                .build();
        MethodSpec del = MethodSpec.methodBuilder("del" + uppercaseModelName)
                .addAnnotation(Override.class)
                .addModifiers(Modifier.PUBLIC)
                .addParameter(Long.class, "id")
                .addStatement("return " + lowercaseModelName + "Repo.removeById(id)")
                .returns(Boolean.class)
                .build();
        MethodSpec update = MethodSpec.methodBuilder("update" + uppercaseModelName)
                .addAnnotation(Override.class)
                .addModifiers(Modifier.PUBLIC)
                .addParameter(doClassName, lowercaseModelName)
                .addStatement("return " + lowercaseModelName + "Repo.updateById(" + lowercaseModelName + ".to" + uppercaseModelName + "Po())")
                .returns(Boolean.class)
                .build();
        MethodSpec get = MethodSpec.methodBuilder("get" + uppercaseModelName)
                .addAnnotation(Override.class)
                .addModifiers(Modifier.PUBLIC)
                .addParameter(Long.class, "id")
                .returns(doClassName)
                .addStatement("return " + uppercaseModelName + ".to" + uppercaseModelName + "(" + lowercaseModelName + "Repo.getById(id))")
                .build();
        List<MethodSpec> methodSpecs = new ArrayList<>();
        methodSpecs.add(add);
        methodSpecs.add(del);
        methodSpecs.add(update);
        methodSpecs.add(get);
        if (tableInfo.getTreeData()) {
            MethodSpec list = MethodSpec.methodBuilder("list" + uppercaseModelName)
                    .addAnnotation(Override.class)
                    .addModifiers(Modifier.PUBLIC)
                    .returns(ParameterizedTypeName.get(
                            ClassName.get(List.class),
                            doClassName))
                    .addStatement("$T<$T> fromList = " + lowercaseModelName + "Repo.list()",
                            ClassName.get(List.class),
                            poClassName)
                    .addStatement("$T<$T> toList = new $T<>()",
                            ClassName.get(List.class),
                            doClassName,
                            ClassName.get(ArrayList.class))
                    .addStatement("fromList.forEach(data -> { toList.add($T.to" + uppercaseModelName + "(data)); })",
                            doClassName)
                    .addStatement("return toList")
                    .build();
            methodSpecs.add(list);
        } else {
            MethodSpec.Builder builder = MethodSpec.methodBuilder("page" + uppercaseModelName);
            builder.addAnnotation(Override.class);
            builder.addModifiers(Modifier.PUBLIC);
            builder.addParameter(doClassName, lowercaseModelName);
            builder.returns(ParameterizedTypeName.get(PageUtil.PageVo.class));
            builder.addStatement("return " + lowercaseModelName + "Repo.page" + uppercaseModelName + "(" + lowercaseModelName + ")");
            MethodSpec page = builder.build();
            methodSpecs.add(page);
        }
        // 创建类
        TypeSpec.Builder builder = TypeSpec.classBuilder(uppercaseModelName + "ServiceImpl")
                .addModifiers(Modifier.PUBLIC)
                .addAnnotation(annotationSpec)
                .addSuperinterface(superInterface)
                .addField(fieldSpec)
                .addMethods(methodSpecs);
        TypeSpec typeSpec = builder.build();

        saveFile(codegen, packageName, typeSpec);
    }


    private void genVo(Codegen codegen) {
        //获取数据表信息
        TableInfo tableInfo = getTableInfo(codegen);

        // 添加包名
        String packageName = codegen.getBasePackage() + ".interfaces.vo." + codegen.getDomain();

        // 添加类名
        String uppercaseModelName = createUppercaseName(codegen.getModelName());

        //添加注解
        List<AnnotationSpec> annotations = getAnnotations(!tableInfo.getTreeData());

        // 添加父类
        TypeName superClass = null;
        if (!tableInfo.getTreeData()) {
            superClass = ParameterizedTypeName.get(PageUtil.PageBase.class);
        }

        //添加字段
        List<FieldSpec> fields = createFields(tableInfo);

        //添加vo转do方法
        ClassName doClassName = ClassName.get(codegen.getBasePackage() + ".domain." + codegen.getDomain() + ".ddo", uppercaseModelName);
        MethodSpec toDo = MethodSpec.methodBuilder("to" + uppercaseModelName)
                .returns(doClassName)
                .addModifiers(Modifier.PUBLIC)
                .addStatement("return $T.toBean($T.toJson(this), $T.class)", JsonUtil.class, JsonUtil.class, doClassName)
                .build();

        // 创建类
        TypeSpec.Builder builder = TypeSpec.classBuilder(uppercaseModelName + "Vo")
                .addModifiers(Modifier.PUBLIC)
                .addFields(fields)
                .addMethod(toDo)
                .addAnnotations(annotations);
        if (null != superClass) {
            builder.superclass(superClass);
        }
        TypeSpec typeSpec = builder.build();

        saveFile(codegen, packageName, typeSpec);
    }

    private List<FieldSpec> createFields(TableInfo tableInfo) {
        List<FieldSpec> fields = new ArrayList<>();
        for (ColumnInfo columnInfo : tableInfo.getColumnInfos()) {
            if (!columnInfo.getColumnName().equals("create_time") && !columnInfo.getColumnName().equals("update_time")) {
                if (columnInfo.getColumnType().toUpperCase().contains("BIGINT")) {
                    fields.add(FieldSpec.builder(returnFieldType(columnInfo.getColumnType()), createLowercaseName(columnInfo.getColumnName()), Modifier.PRIVATE)
                            .addAnnotation(AnnotationSpec.builder(JsonFormat.class).addMember("shape", "$T.Shape.STRING", JsonFormat.class).build()).build());
                } else {
                    fields.add(FieldSpec.builder(returnFieldType(columnInfo.getColumnType()), createLowercaseName(columnInfo.getColumnName()), Modifier.PRIVATE).build());
                }
            }
        }
        return fields;
    }


    private void genController(Codegen codegen) {
        //获取数据表信息
        TableInfo tableInfo = getTableInfo(codegen);

        // 添加包名
        String packageName = codegen.getBasePackage() + ".interfaces.facade.inner";

        // 添加类名
        String uppercaseModelName = createUppercaseName(codegen.getModelName());
        String lowercaseModelName = createLowercaseName(codegen.getModelName());
        String constantModelName = codegen.getModelName().toUpperCase();
        String urlModelName = codegen.getModelName().replace("_", "-");

        // 添加注解
        List<AnnotationSpec> annotations = new ArrayList<>();
        annotations.add(AnnotationSpec.builder(Tag.class).addMember("name", "$S", codegen.getDomain() + " inner api").build());
        annotations.add(AnnotationSpec.builder(RestController.class).build());
        annotations.add(AnnotationSpec.builder(RequestMapping.class).addMember("value", "$S", "/inner-api/" + codegen.getService()).build());

        // 添加字段
        FieldSpec base = FieldSpec.builder(String.class, constantModelName)
                .addModifiers(Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL)
                .initializer("\"/" + urlModelName + "\"")
                .build();
        FieldSpec service = FieldSpec.builder(
                        ClassName.get(codegen.getBasePackage() + ".domain." + codegen.getDomain() + ".service", uppercaseModelName + "Service"),
                        lowercaseModelName + "Service",
                        Modifier.PRIVATE)
                .addAnnotation(Resource.class)
                .build();

        List<FieldSpec> fieldSpecs = new ArrayList<>();
        fieldSpecs.add(base);
        fieldSpecs.add(service);

        // 添加方法
        Class<ResBody> resBodyClass = ResBody.class;
        ClassName voClassName = ClassName.get(codegen.getBasePackage() + ".interfaces.vo." + codegen.getDomain(), uppercaseModelName + "Vo");
        MethodSpec addMethod = MethodSpec.methodBuilder("add" + uppercaseModelName)
                .addAnnotation(AnnotationSpec.builder(PermissionCheck.class)
                        .addMember("value", "$S", "sys:" + urlModelName + ":add")
                        .build())
                .addAnnotation(AnnotationSpec.builder(PostMapping.class)
                        .addMember("value", "$L", constantModelName)
                        .build())
                .addModifiers(Modifier.PUBLIC)
                .returns(resBodyClass)
                .addParameter(ParameterSpec.builder(voClassName, lowercaseModelName + "Vo")
                        .addAnnotation(RequestBody.class)
                        .build())
                .addStatement(lowercaseModelName + "Vo.setId(null)")
                .addStatement("$T result = " + lowercaseModelName + "Service.add" + uppercaseModelName + "(" + lowercaseModelName + "Vo.to" + uppercaseModelName + "())",
                        long.class)
                .addStatement("return result >  0 ? $T.builder().build().ok() : $T.builder().build().fail()",
                        resBodyClass,
                        resBodyClass)
                .build();
        MethodSpec delMethod = MethodSpec.methodBuilder("del" + uppercaseModelName)
                .addAnnotation(AnnotationSpec.builder(PermissionCheck.class)
                        .addMember("value", "$S", "sys:" + urlModelName + ":del")
                        .build())
                .addAnnotation(AnnotationSpec.builder(DeleteMapping.class)
                        .addMember("value", "$N + $S", constantModelName, "/{id}")
                        .build())
                .addModifiers(Modifier.PUBLIC)
                .returns(resBodyClass)
                .addParameter(ParameterSpec.builder(long.class, "id")
                        .addAnnotation(AnnotationSpec.builder(PathVariable.class)
                                .addMember("value", "$S", "id")
                                .build())
                        .build())
                .addStatement("$T result = " + lowercaseModelName + "Service.del" + uppercaseModelName + "(id)", Boolean.class)
                .addStatement("return result ? $T.builder().build().ok() : $T.builder().build().fail()",
                        resBodyClass,
                        resBodyClass)
                .build();
        MethodSpec updateMethod = MethodSpec.methodBuilder("update" + uppercaseModelName)
                .addAnnotation(AnnotationSpec.builder(PermissionCheck.class)
                        .addMember("value", "$S", "sys:" + urlModelName + ":update")
                        .build())
                .addAnnotation(AnnotationSpec.builder(PutMapping.class)
                        .addMember("value", "$L", constantModelName)
                        .build())
                .addModifiers(Modifier.PUBLIC)
                .returns(resBodyClass)
                .addParameter(ParameterSpec.builder(voClassName, lowercaseModelName + "Vo")
                        .addAnnotation(RequestBody.class)
                        .build())
                .addStatement("$T result = " + lowercaseModelName + "Service.update" + uppercaseModelName + "(" + lowercaseModelName + "Vo.to" + uppercaseModelName + "())",
                        boolean.class)
                .addStatement("return result ? $T.builder().build().ok() : $T.builder().build().fail()",
                        resBodyClass,
                        resBodyClass)
                .build();

        MethodSpec getMethod = MethodSpec.methodBuilder("get" + uppercaseModelName)
                .addAnnotation(AnnotationSpec.builder(PermissionCheck.class)
                        .addMember("value", "$S", "sys:" + urlModelName + ":get")
                        .build())
                .addAnnotation(AnnotationSpec.builder(GetMapping.class)
                        .addMember("value", "$N + $S", constantModelName, "/{id}")
                        .build())
                .addModifiers(Modifier.PUBLIC)
                .returns(resBodyClass)
                .addParameter(ParameterSpec.builder(Long.class, "id")
                        .addAnnotation(AnnotationSpec.builder(PathVariable.class)
                                .addMember("value", "$S", "id")
                                .build())
                        .build())
                .addStatement("return $T.builder().build().ok(" + lowercaseModelName + "Service.get" + uppercaseModelName + "(id))",
                        resBodyClass)
                .build();


        List<MethodSpec> methodSpecs = new ArrayList<>();
        methodSpecs.add(addMethod);
        methodSpecs.add(delMethod);
        methodSpecs.add(updateMethod);
        methodSpecs.add(getMethod);

        if (tableInfo.getTreeData()) {
            MethodSpec tree = MethodSpec.methodBuilder("tree" + uppercaseModelName)
                    .addAnnotation(AnnotationSpec.builder(PermissionCheck.class)
                            .addMember("value", "$S", "sys:" + urlModelName + ":tree")
                            .build())
                    .addAnnotation(AnnotationSpec.builder(PostMapping.class)
                            .addMember("value", "$N + $S", constantModelName, "/tree")
                            .build())
                    .addModifiers(Modifier.PUBLIC)
                    .returns(resBodyClass)
                    .addStatement("return $T.builder().build().ok($T.toTree(" + lowercaseModelName + "Service.list" + uppercaseModelName + "(), 0L))", resBodyClass, ClassName.get(TreeUtil.class))
                    .build();
            methodSpecs.add(tree);
        } else {
            MethodSpec pageMethod = MethodSpec.methodBuilder("page" + uppercaseModelName)
                    .addAnnotation(AnnotationSpec.builder(PermissionCheck.class)
                            .addMember("value", "$S", "sys:" + urlModelName + ":page")
                            .build())
                    .addAnnotation(AnnotationSpec.builder(PostMapping.class)
                            .addMember("value", "$N + $S", constantModelName, "/page")
                            .build())
                    .addModifiers(Modifier.PUBLIC)
                    .returns(resBodyClass)
                    .addParameter(ParameterSpec.builder(voClassName, lowercaseModelName + "Vo")
                            .addAnnotation(RequestBody.class)
                            .build())
                    .addStatement("return $T.builder().build().ok(" + lowercaseModelName + "Service.page" + uppercaseModelName + "(" + lowercaseModelName + "Vo.to" + uppercaseModelName + "()))",
                            resBodyClass)
                    .build();
            methodSpecs.add(pageMethod);
        }

        // 创建类
        TypeSpec.Builder builder = TypeSpec.classBuilder(StrUtil.capitalizeFirstLetter(codegen.getDomain()) + "InnerController")
                .addModifiers(Modifier.PUBLIC)
                .addFields(fieldSpecs)
                .addMethods(methodSpecs)
                .addAnnotations(annotations);
        TypeSpec typeSpec = builder.build();

        saveFile(codegen, packageName, typeSpec);
    }


    private void genPage(Codegen codegen) {
        //获取数据表信息
        TableInfo tableInfo = getTableInfo(codegen);

        List<ColumnInfo> columnInfos = tableInfo.getColumnInfos();
        List<ColumnInfo> freemarkerColumnInfos = new ArrayList<>();
        columnInfos.forEach(columnInfo -> {
            ColumnInfo freemarkerColumnInfo = ColumnInfo.builder().columnName(createLowercaseName(columnInfo.getColumnName())).columnComment(columnInfo.getColumnComment()).build();
            freemarkerColumnInfos.add(freemarkerColumnInfo);
        });

        // 配置Freemarker
        Configuration cfg = new Configuration(Configuration.VERSION_2_3_31);
        try {
            // 使用ClassTemplateLoader从类路径加载模板
            cfg.setTemplateLoader(new ClassTemplateLoader(this.getClass(), "/templates"));
            // 生成输出(page)
            try (FileWriter writer = new FileWriter(new File("/code/index.vue"))) {
                // 创建数据模型
                Map<String, Object> model = new HashMap<>();
                model.put("authInfo", codegen.getModelName().replace("_", "-"));
                model.put("uppercaseName", createUppercaseName(codegen.getModelName()));
                model.put("apiPath", codegen.getTableName().replace("_", "/"));
                model.put("columnInfos", freemarkerColumnInfos);
                // 加载模板文件
                Template pageTemplate = tableInfo.getTreeData() ? cfg.getTemplate("tree.ftl") : cfg.getTemplate("page.ftl");
                pageTemplate.process(model, writer);
            } catch (TemplateException | IOException e) {
                log.error(e.getMessage());
            }
            // 生成输出(tree)
        } catch (Exception e) {
            log.error(e.getMessage());
        }

    }

    private TableInfo getTableInfo(Codegen codegen) {
        if (null != codegen.getTableInfo()) {
            return codegen.getTableInfo();
        }
        try (Connection connection = DriverManager.getConnection(codegen.getUrl(), codegen.getUser(), codegen.getPassword()); Statement stmt = connection.createStatement()) {
            String query = "SELECT column_name, column_type, column_comment " +
                    "FROM information_schema.columns " +
                    "WHERE table_name = '" + codegen.getTableName() + "'";
            TableInfo tableInfo = new TableInfo();
            tableInfo.setTableName(codegen.getTableName());
            tableInfo.setColumnInfos(new ArrayList<>());
            ResultSet rs = stmt.executeQuery(query);
            while (rs.next()) {
                String columnName = rs.getString("column_name");
                String columnType = rs.getString("column_type");
                String columnComment = rs.getString("column_comment");
                ColumnInfo columnInfo = ColumnInfo.builder().columnName(columnName).columnType(columnType).columnComment(columnComment).build();
                tableInfo.getColumnInfos().add(columnInfo);
                //如果有parent_id字段默认表格数据是树形数据
                if ("parent_id".equals(columnName)) {
                    tableInfo.setTreeData(true);
                }
            }
            codegen.setTableInfo(tableInfo);
            return tableInfo;
        } catch (SQLException e) {
            log.error("getTableInfo-error", e);
            throw new RuntimeException(e);
        }
    }

    private String createUppercaseName(String tableName) {
        String[] tableNameArr = tableName.split("_");
        StringBuilder poName = new StringBuilder();
        for (String tableNamePart : tableNameArr) {
            poName.append(Character.toUpperCase(tableNamePart.charAt(0))).append(tableNamePart.substring(1));
        }
        return poName.toString();
    }

    private String createLowercaseName(String column) {
        String name = createUppercaseName(column);
        return Character.toLowerCase(name.charAt(0)) + name.substring(1);
    }


    private Type returnFieldType(String columnType) {
        String upperCaseColumnType = columnType.toUpperCase();
        if (upperCaseColumnType.contains("CHAR") || upperCaseColumnType.contains("VARCHAR") || upperCaseColumnType.contains("TEXT")) {
            return String.class;
        } else if (upperCaseColumnType.contains("BIGINT")) {
            return Long.class;
        } else if (upperCaseColumnType.contains("INT") || upperCaseColumnType.contains("SMALLINT") || upperCaseColumnType.contains("TINYINT")) {
            return Integer.class;
        } else if (upperCaseColumnType.contains("DECIMAL")) {
            return BigDecimal.class;
        } else if (upperCaseColumnType.contains("FLOAT")) {
            return Float.class;
        } else if (upperCaseColumnType.contains("DOUBLE")) {
            return Double.class;
        } else if (upperCaseColumnType.contains("DATETIME")) {
            return LocalDateTime.class;
        }
        throw new RuntimeException("column type:" + columnType + " is not support！");
    }

    private boolean filterColumnName(String columnName) {
        return switch (columnName) {
            case "id", "create_time", "update_time", "del_flag", "parent_id", "sort", "level" -> false;
            default -> true;
        };
    }

    private List<AnnotationSpec> getAnnotations(Boolean containEqualsAndHashCode) {
        List<AnnotationSpec> annotationSpecs = new ArrayList<>();
        annotationSpecs.add(AnnotationSpec.builder(Data.class).build());
        annotationSpecs.add(AnnotationSpec.builder(Builder.class).build());
        annotationSpecs.add(AnnotationSpec.builder(AllArgsConstructor.class).build());
        annotationSpecs.add(AnnotationSpec.builder(NoArgsConstructor.class).build());
        if (containEqualsAndHashCode) {
            annotationSpecs.add(AnnotationSpec.builder(EqualsAndHashCode.class).addMember("callSuper", "$L", true).build());
        }
        return annotationSpecs;
    }

    private String compressCodeFile() {
        zipFolder("/code", "/code.zip");
        return "/code.zip";
    }

    private void removeCodeFile(String path) {
        // 添加要删除的嵌套目录路径
        File directory = new File(path);
        try {
            if (directory.isFile()) {
                FileUtils.delete(directory);
            } else {
                FileUtils.deleteDirectory(directory);
            }
        } catch (IOException e) {
            log.error("removeCodeFile-error", e);
        }
    }

    private void saveFile(Codegen codegen, String packageName, TypeSpec typeSpec) {
        // 创建Java文件
        JavaFile javaFile = JavaFile.builder(packageName, typeSpec).build();
        // 输出Java文件到控制台
        try {
            javaFile.writeTo(Paths.get(codegen.getSavePath()));
            javaFile.writeTo(System.out);
        } catch (Exception e) {
            log.error(e.getMessage());
        }
    }

    public static void zipFolder(String sourceFolderPath, String zipPath) {
        try (FileOutputStream fos = new FileOutputStream(zipPath);
             ZipOutputStream zos = new ZipOutputStream(fos)) {

            Path sourcePath = Paths.get(sourceFolderPath);
            Files.walk(sourcePath).forEach(path -> {
                try {
                    if (Files.isDirectory(path)) {
                        return; // 忽略文件夹，只压缩文件
                    }
                    // 获取文件名和相对路径
                    String zipEntryName = sourcePath.relativize(path).toString();
                    ZipEntry zipEntry = new ZipEntry(zipEntryName);
                    zos.putNextEntry(zipEntry);

                    // 写文件内容到 ZIP 条目
                    try (InputStream inputStream = Files.newInputStream(path)) {
                        byte[] buffer = new byte[1024];
                        int length;
                        while ((length = inputStream.read(buffer)) >= 0) {
                            zos.write(buffer, 0, length);
                        }
                    }
                    zos.closeEntry();
                } catch (IOException e) {
                    System.err.println("Error while processing file: " + path + " - " + e.getMessage());
                }
            });
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        System.out.println("Folder " + sourceFolderPath + " has been successfully compressed to " + zipPath);
    }

}
